import { WhereValue } from "./HashMap";
import { Op } from "./Operators";

export class OpFunc {
    private static _inst: OpFunc;
    static get inst() { return this._inst || new OpFunc() }
    private _hash: any;
    constructor() {
        this._hash = {};
        this._registFunc(Op.gt, this._gtFunc);
        this._registFunc(Op.gte, this._gteFunc);
        this._registFunc(Op.between, this._betweenFunc);
        this._registFunc(Op.lt, this._ltFunc);
        this._registFunc(Op.lte, this._lteFunc);
        this._registFunc(Op.ne, this._neFunc);
        this._registFunc(Op.notBetween, this._notBetweenFunc);
    }

    private _registFunc(funcName: symbol, func: (value: WhereValue, currValue: WhereValue) => boolean) {
        this._hash[funcName] = func;
    }

    check<T>(funcName: symbol, value: WhereValue, currValue: WhereValue): boolean {
        let func: Function = this._hash[funcName];
        if (func) {
            return func.call(this, value, currValue);
        }
        return true;
    }

    private _gtFunc(value: WhereValue, currValue: WhereValue) {
        return value > currValue;
    }

    private _gteFunc(value: WhereValue, currValue: WhereValue) {
        return value >= currValue;
    }

    private _betweenFunc(value: WhereValue, currValue: WhereValue) {
        return value >= currValue[0] && value <= currValue[1];
    }

    private _ltFunc(value: WhereValue, currValue: WhereValue) {
        return value < currValue;
    }

    private _lteFunc(value: WhereValue, currValue: WhereValue) {
        return value <= currValue;
    }

    private _neFunc(value: WhereValue, currValue: WhereValue) {
        return value != currValue;
    }

    private _notBetweenFunc(value: WhereValue, currValue: WhereValue) {
        return value <= currValue[0] || value >= currValue[1];
    }
}

export interface WhereOperators {
    [Op.gt]?: number | string;
    [Op.gte]?: number | string;
    [Op.lt]?: number | string;
    [Op.lte]?: number | string;
    [Op.between]?: Array<number | string>;
    [Op.notBetween]?: Array<number | string>;
    [Op.ne]?: number | string;
}